﻿Option Explicit On
Option Strict On

Imports System
Imports System.Text
Imports System.Collections

Public MustInherit Class ConfigSourceBase
    Implements IConfigSource

#Region " Private Memebers "

    Private m_sourceList As New ArrayList()
    Private m_configList As ConfigCollection = Nothing
    Private m_autoSave As Boolean = False
    Private m_alias As New AliasText()

#End Region

#Region " Public Events "

    Public Event Reloaded As EventHandler Implements IConfigSource.Reloaded
    Public Event Saved As EventHandler Implements IConfigSource.Saved

#End Region

#Region " Constructors "

    ''' <summary>
    ''' Default Constructor
    ''' </summary>
    ''' <remarks></remarks>
    Protected Sub New()
        m_configList = New ConfigCollection(Me)
    End Sub

#End Region

#Region " Public Properties "

    Public ReadOnly Property Configs() As ConfigCollection Implements IConfigSource.Configs
        Get
            Return m_configList
        End Get
    End Property

    Public Property AutoSave() As Boolean Implements IConfigSource.AutoSave
        Get
            Return m_autoSave
        End Get
        Set(value As Boolean)
            m_autoSave = value
        End Set
    End Property

    Public ReadOnly Property Aliases() As AliasText Implements IConfigSource.Aliases
        Get
            Return m_alias
        End Get
    End Property
#End Region

#Region " Public Methods "

    Public Sub Merge(source As IConfigSource) Implements IConfigSource.Merge
        If source Is Nothing Then
            Throw New ArgumentNullException("source")
        End If

        If Not m_sourceList.Contains(source) Then
            m_sourceList.Add(source)
        End If

        For Each config As IConfig In source.Configs
            Me.Configs.Add(config)
        Next
    End Sub

    Public Overridable Function AddConfig(name As String) As IConfig Implements IConfigSource.AddConfig
        Return m_configList.Add(name)
    End Function

    Public Function GetExpanded(config As IConfig, key As String) As String Implements IConfigSource.GetExpanded
        Return Expand(config, key, False)
    End Function

    Public Overridable Sub Save() Implements IConfigSource.Save
        OnSaved(New EventArgs())
    End Sub

    Public Overridable Sub Reload() Implements IConfigSource.Reload
        OnReloaded(New EventArgs())
    End Sub

    Public Sub ExpandKeyValues() Implements IConfigSource.ExpandKeyValues
        Dim keys As String() = Nothing

        For Each config As IConfig In m_configList
            keys = config.GetKeys()
            For i As Integer = 0 To keys.Length - 1
                Expand(config, keys(i), True)
            Next
        Next
    End Sub

    Public Sub ReplaceKeyValues() Implements IConfigSource.ReplaceKeyValues
        ExpandKeyValues()
    End Sub

#End Region

#Region " Protected Methods "

    Protected Sub OnReloaded(e As EventArgs)
        RaiseEvent Reloaded(Me, e)
    End Sub

    Protected Sub OnSaved(e As EventArgs)
        RaiseEvent Saved(Me, e)
    End Sub

#End Region

#Region " Private Methods "

    ''' <summary>
    ''' Expands key values from the given IConfig.
    ''' </summary>
    ''' <param name="config"></param>
    ''' <param name="key"></param>
    ''' <param name="setValue"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function Expand(config As IConfig, key As String, setValue As Boolean) As String
        Dim result As String = config.GetValue(key)
        If result Is Nothing Then
            Throw New ArgumentException(String.Format(Globalization.CultureInfo.InvariantCulture, "[{0}] not found in [{1}]", key, config.Name))
        End If

        While (True)
            Dim startIndex As Integer = result.IndexOf("${", 0, StringComparison.Ordinal)
            If startIndex = -1 Then
                Exit While
            End If

            Dim endIndex As Integer = result.IndexOf("}", startIndex + 2, StringComparison.Ordinal)
            If endIndex = -1 Then
                Exit While
            End If

            Dim search As String = result.Substring(startIndex + 2, endIndex - (startIndex + 2))

            If search = key Then
                ' Prevent infinite recursion
                Throw New ArgumentException("Key cannot have a expand value of itself: " + key)
            End If

            Dim replace As String = ExpandValue(config, search)

            result = result.Replace("${" + search + "}", replace)
        End While

        If setValue Then
            config.SetValue(key, result)
        End If

        Return result
    End Function

    ''' <summary>
    ''' Returns the replacement value of a config.
    ''' </summary>
    ''' <param name="config"></param>
    ''' <param name="search"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function ExpandValue(config As IConfig, search As String) As String
        Dim result As String = Nothing

        Dim replaces As String() = search.Split("|"c)

        If replaces.Length > 1 Then
            Dim newConfig As IConfig = Me.Configs(replaces(0))
            If newConfig Is Nothing Then
                Throw New ArgumentException("Expand config not found: " + replaces(0))
            End If
            result = newConfig.GetValue(replaces(1))
            If result Is Nothing Then
                Throw New ArgumentException("Expand key not found: " + replaces(1))
            End If
        Else
            result = config.GetValue(search)

            If result Is Nothing Then
                Throw New ArgumentException("Key not found: " + search)
            End If
        End If

        Return result
    End Function

#End Region

End Class
